#include <android/log.h>

#include "lib/drivers/pvmdriver_android_audiotrack.h"
#include "lib/drivers/pvmdeviceinterface.h"
#include "lib/drivers/pvmdriver_AndroideAudioTrack_jni.h"

#include "lib/pvmerrors.h"
#include "lib/pvmstrings.h"
#include "lib/pvmdriver.h"

#include "lib/memory/pvmmemory.h"

#define PVMDAUDIOTRACK_WAIT_FOR_SUSPEND		1000
#define PVMDAUDIOTRACK_DEFAULT_BUFFER_LENGTH	200
#define PVMDAUDIOTRACK_DEVICE_TYPE	EPVM_DTYPE_OUTPUT|EPVM_DTYPE_DEVICE


static float frequency = 440;
static float increment = (float)(2*3,14159265) * frequency / 22050; // angular increment for each sample
static float angle = 0;

class PVMDeviceAndroidAudioTrack : public IPVMDeviceOutSimple
{
public:
	PVMDeviceAndroidAudioTrack(PVMDriverAndroidAudioTrack* driver);
	~PVMDeviceAndroidAudioTrack();

	virtual pvm_uint32 GetDeviceType();
	virtual void DeviceClose();
	virtual pvm_bool DeviceOpened();

	virtual pvm_bool SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize);
	virtual pvm_bool GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize);

	virtual void Suspend();
	virtual pvm_bool IsSuspended();
	virtual void Resume();

	virtual void DeviceNotify();

	virtual void SetBufferLengthMs(pvm_uint32 bufferSize);
	virtual pvm_uint32 GetBufferRealLengthMs();

	virtual pvm_int32 GetCPUUsage();

	virtual pvm_uint32 GetChanelsNum();
	virtual pvm_uint32 GetBitsPerSample();
	virtual pvm_uint32 GetSamplesPerSecond();

	pvm_bool Init(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended);
	virtual IPVMDriverBase* GetDriver() {return driver;}

	friend int fillBufferFromAudioTrackDevice(void* device, void* buffer, int len);
protected:
	void DeInit();
	pvm_bool DeviceHasMoreData();
	int fillDataToBuffer(void* buffer, int sizeInBytes);
	int getCurrentBufferSizeInBytes();
protected:
	enum EConsts{
		NUM_BUFFERS=4,
	};

	pvm_bool isDeviceInitialized;

	PVMCriticalSection pvmdGlobalVarGuard;

	//wave out stuff
	pvm_uint32			currentSampleRate;
	pvm_uint32			currentbitsPerSample;
	pvm_uint32			currentbytesPerSample;
	pvm_uint32			currentChanelsNum;

	void* javaDriverPtr;

	IPVMDeviceOutSimpleWaveSource *waveSource;
	PVMDriverAndroidAudioTrack* driver;
};

pvm_uint32 PVMDeviceAndroidAudioTrack::GetChanelsNum()
{
	return currentChanelsNum;
}

pvm_uint32 PVMDeviceAndroidAudioTrack::GetBitsPerSample()
{
	return currentbitsPerSample;
}

pvm_uint32 PVMDeviceAndroidAudioTrack::GetSamplesPerSecond()
{
	return currentSampleRate;
}

pvm_int32 PVMDeviceAndroidAudioTrack::GetCPUUsage()
{
	return -1;
}

PVMDeviceAndroidAudioTrack::PVMDeviceAndroidAudioTrack(PVMDriverAndroidAudioTrack* driver)
{
	isDeviceInitialized = false;

	currentSampleRate = 0;
	currentbitsPerSample = 0;
	currentbytesPerSample = 0;
	currentChanelsNum = 0;

	waveSource = NULL;

	javaDriverPtr = NULL;
	this->driver = driver;
}

PVMDeviceAndroidAudioTrack::~PVMDeviceAndroidAudioTrack()
{
	DeInit();
}

pvm_bool PVMDeviceAndroidAudioTrack::Init(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended)
{
	PVMInitCritical(&pvmdGlobalVarGuard);
	PVMEnterCritical(&pvmdGlobalVarGuard);
	
	isDeviceInitialized = false;

	currentSampleRate = samplesPerSec;
	currentbitsPerSample = bitsPerSample;
	currentbytesPerSample = bitsPerSample/8;
	currentChanelsNum = chanelsNum;

	this->waveSource = waveSource;
	void* createParam = reinterpret_cast<void*>(this);
	javaDriverPtr = pvmCreateJavaDriver(createParam, currentSampleRate, currentbitsPerSample, currentChanelsNum==1?0:1, PVMDAUDIOTRACK_DEFAULT_BUFFER_LENGTH);
	PVMLeaveCritical(&pvmdGlobalVarGuard);
	if(javaDriverPtr==0)
	{
		this->waveSource = NULL;
		return false;
	}

	isDeviceInitialized = true;
	DeviceNotify();
	return true;
}

void PVMDeviceAndroidAudioTrack::DeInit()
{
	if(isDeviceInitialized)
	{
		PVMEnterCritical(&pvmdGlobalVarGuard);
		destroyJavaDriver(javaDriverPtr);
		javaDriverPtr = NULL;
		isDeviceInitialized = false;
		PVMLeaveCritical(&pvmdGlobalVarGuard);
		PVMDeinitCritical(&pvmdGlobalVarGuard);
	}

	waveSource = NULL;
}

pvm_uint32 PVMDeviceAndroidAudioTrack::GetDeviceType()
{
	return PVMDAUDIOTRACK_DEVICE_TYPE;
}

void PVMDeviceAndroidAudioTrack::DeviceClose()
{
	DeInit();
	delete this;
}

pvm_bool PVMDeviceAndroidAudioTrack::DeviceOpened()
{
	return isDeviceInitialized;
}

pvm_bool PVMDeviceAndroidAudioTrack::SetOption(const pvm_char* optionName, void* param, pvm_uint32 paramSize)
{
	return false;
}

pvm_bool PVMDeviceAndroidAudioTrack::GetOption(const pvm_char* optionName, void* param, pvm_uint32* paramSize)
{
	return false;
}

void PVMDeviceAndroidAudioTrack::Suspend()
{
	PVMEnterCritical(&pvmdGlobalVarGuard);
	javaDriverSuspendDevice(javaDriverPtr);
	PVMLeaveCritical(&pvmdGlobalVarGuard);
}

pvm_bool PVMDeviceAndroidAudioTrack::IsSuspended()
{
	return javaDriverIsDeviceSuspended(javaDriverPtr);
}

void PVMDeviceAndroidAudioTrack::Resume()
{
	PVMEnterCritical(&pvmdGlobalVarGuard);
	javaDriverResumeDevice(javaDriverPtr);
	PVMLeaveCritical(&pvmdGlobalVarGuard);
}

void PVMDeviceAndroidAudioTrack::DeviceNotify()
{
	PVMEnterCritical(&pvmdGlobalVarGuard);
	javaDriverNotifyDevice(javaDriverPtr);
	PVMLeaveCritical(&pvmdGlobalVarGuard);
}

void PVMDeviceAndroidAudioTrack::SetBufferLengthMs(pvm_uint32 bufferSize)
{
	if(waveSource==NULL || javaDriverPtr==NULL)
		return;

	javaDriverSetCurrentBufferLengthMS(javaDriverPtr, bufferSize);
	int newBufferSizeB =getCurrentBufferSizeInBytes();
	if(newBufferSizeB>0)
		waveSource->ResizeRenderBuffer(newBufferSizeB);

	DeviceNotify();
}

pvm_uint32 PVMDeviceAndroidAudioTrack::GetBufferRealLengthMs()
{
	return javaDriverGetCurrentBufferLengthMS(javaDriverPtr);
}

PVM_FORCEINLINE pvm_bool PVMDeviceAndroidAudioTrack::DeviceHasMoreData()
{
	return waveSource->HasMoreData();
}

int PVMDeviceAndroidAudioTrack::fillDataToBuffer(void* buffer, int sizeInBytes)
{
	if(waveSource==NULL)
	{
		return 0;
	}

	pvm_uint32 filled = 0;
	filled = waveSource->FillBuffer(buffer, sizeInBytes / currentbytesPerSample) * currentbytesPerSample;
	return filled;
}

int PVMDeviceAndroidAudioTrack::getCurrentBufferSizeInBytes()
{
	return javaDriverGetCurrentBufferLengthInBytes(javaDriverPtr);
}

////////////////////////////////////////////////////////////////////////////////
//PVMDriverAndroidAudioTrack														  //
////////////////////////////////////////////////////////////////////////////////

PVMDriverAndroidAudioTrack::PVMDriverAndroidAudioTrack()
{
}

PVMDriverAndroidAudioTrack::~PVMDriverAndroidAudioTrack()
{
}

pvm_bool PVMDriverAndroidAudioTrack::Init()
{

	return true;
}

pvm_bool PVMDriverAndroidAudioTrack::IsInitialized()
{
	return true;
}

void PVMDriverAndroidAudioTrack::DriverClose()
{
	delete this;
}

pvm_uint32 PVMDriverAndroidAudioTrack::GetDriverType()
{
	return PVMDAUDIOTRACK_DEVICE_TYPE;
}

pvm_bool PVMDriverAndroidAudioTrack::GetDriverOption(const pvm_char* optionName, void* optionVal, pvm_uint32* optionSize)
{
	return false;
}

pvm_bool PVMDriverAndroidAudioTrack::SetDriverOption(const pvm_char* optionName, void* optionVal, pvm_uint32 optionSize)
{
	return false;
}


pvm_uint32 PVMDriverAndroidAudioTrack::DevicesNum()
{
	return 1;
}

pvm_int32 PVMDriverAndroidAudioTrack::DeviceName(pvm_int32 nDevice, pvm_char* buffer, pvm_uint32 maxLength)
{
	if(nDevice!=0&&nDevice!=-1)
		return -1;

	const pvm_char* deviceName = _PVM_STR("default audio device");
	int deviceNameLen = pvm_strlen(deviceName);

	if(buffer==NULL||maxLength==0)
	{
		return deviceNameLen + 1;
	}

	pvm_strcpy(buffer, deviceName);

	return deviceNameLen;
}

pvm_bool PVMDriverAndroidAudioTrack::DeviceEnumModes(pvm_int32 nDeivce, TFormatEnumCallbackProc enumProc, void *param)
{
	if(!enumProc)
		return false;

	enumProc(1, 8, 11025, param);
	enumProc(1, 8, 22050, param);
	enumProc(1, 8, 44100, param);
	enumProc(1, 8, 48000, param);

	enumProc(1, 16, 11025, param);
	enumProc(1, 16, 22050, param);
	enumProc(1, 16, 44100, param);
	enumProc(1, 16, 48000, param);

	enumProc(2, 8, 11025, param);
	enumProc(2, 8, 22050, param);
	enumProc(2, 8, 44100, param);
	enumProc(2, 8, 48000, param);

	enumProc(2, 16, 11025, param);
	enumProc(2, 16, 22050, param);
	enumProc(2, 16, 44100, param);
	enumProc(2, 16, 48000, param);

	return true;
}

IPVMDeviceOutSimple* PVMDriverAndroidAudioTrack::DeviceOpen(pvm_uint32 samplesPerSec, pvm_uint32 bitsPerSample, pvm_uint32 chanelsNum, pvm_int32 deviceNum, void *additionalInfo, pvm_uint32 infoSize, IPVMDeviceOutSimpleWaveSource *waveSource, pvm_bool deviceSuspended)
{
	if(!IsInitialized())
		return NULL;

	PVMDeviceAndroidAudioTrack* device = new PVMDeviceAndroidAudioTrack(this);
	if(!device)
		return NULL;
	if(!device->Init(samplesPerSec, bitsPerSample, chanelsNum, deviceNum, additionalInfo, infoSize, waveSource, deviceSuspended))
	{
		device->DeviceClose();
		return NULL;
	}

	IPVMDeviceOutSimple* retVal = static_cast<IPVMDeviceOutSimple*>(device);

	return retVal;
}


void PVMDriverAndroidAudioTrack::CloseDevice(IPVMDeviceBase* device)
{
	if(device!=NULL)
		device->DeviceClose();
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

IPVMDriverOut* PVMDriverCreateAndroidAudioTrack()
{
	PVMDriverAndroidAudioTrack* outDrv = new PVMDriverAndroidAudioTrack();
	if(!outDrv)
		return NULL;
	PVMDriverSimple* simpledrv = new PVMDriverSimple(outDrv);
	
	if(!simpledrv)
	{
		outDrv->DriverClose();
	}

	return simpledrv;
}

static pvm_int32 DriverNameShort(pvm_char* buffer, pvm_uint32 maxLength)
{
	return PVMCopyDriverName(_PVM_STR("Android AudioTrack"), buffer, maxLength);
}

static pvm_int32 DriverNameFull(pvm_char* buffer, pvm_uint32 maxLength)
{
	return PVMCopyDriverName(_PVM_STR("Android AudioTrack (software mixing)"), buffer, maxLength);
}

static pvm_uint32 DriverTypeAndFlags()
{
	return PVMDAUDIOTRACK_DEVICE_TYPE;
}

static IPVMDriverBase* DriverLoad()
{
	return PVMDriverCreateAndroidAudioTrack();
}

IPVMDriverDesc	androidAudioTrackDesc;

IPVMDriverDesc* PVMGetDriverDescAndroidAudioTrack()
{
	androidAudioTrackDesc.DriverNameShort = DriverNameShort;
	androidAudioTrackDesc.DriverNameFull = DriverNameFull;
	androidAudioTrackDesc.DriverTypeAndFlags = DriverTypeAndFlags;
	androidAudioTrackDesc.DriverLoad = DriverLoad;
	return &androidAudioTrackDesc;
}

int fillBufferFromAudioTrackDevice(void* device, void* buffer, int len)
{
	if(device==0)
	{
		__android_log_print(ANDROID_LOG_DEBUG, "pvmdriver_android_audiotrack.cpp", "fillBufferFromAudioTrackDevice - device==NULL");
		return 0;
	}
	int filled = 0;
	PVMDeviceAndroidAudioTrack* devicePtr = static_cast<PVMDeviceAndroidAudioTrack*>(device);
	filled = devicePtr->fillDataToBuffer(buffer, len);
	return filled;
	
	/*short int *samples = (short int*)buffer;
	int sizeInSamples = len / 2;
	
	for( int i = 0; i < sizeInSamples; i+=2)
	{
		samples[i] = (int)(32767 * sinf(angle));
		angle += increment;
		samples[i + 1] = samples[i];
	}

	/*char* samples = (char*)buffer;
	int sizeInSamples = len;

	for(int i = 0; i < sizeInSamples; i+=8)
	{
		samples[i] = 0;
		samples[i + 1] = 0;
		samples[i + 2] = 0;
		samples[i + 3] = 0;

		samples[i + 4] = 0x65;
		samples[i + 5] = 0x64;
		samples[i + 6] = 0x65;
		samples[i + 7] = 0x64;
	}*/


	return len;
}

#undef	PVMDAUDIOTRACK_WAIT_FOR_SUSPEND
#undef	PVMDAUDIOTRACK_DEFAULT_BUFFER_LENGTH
#undef	PVMDAUDIOTRACK_DEVICE_TYPE
